﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;

namespace game
{
    public class HookCamera : Camera
    {
        public Vector3 Offset { get; set; }
        public Matrix Orientation { get; set; }
        public GameObject HookedNode { get; set; }

        protected bool shaking = false;

        protected int elapsedShakeTimeMs;
        protected int shakeMaxTimeMs = 600;
        protected int shakePhaseTimeMs = 100;
        protected float halfShakePhaseTimeInv;
        protected float shakeStrengthFactor = 700.0f;

        protected float currentShakeStrength;
        protected Vector3 currentShakeAxis;
        protected int currentShakeTimeMs;

        public HookCamera(Vector3 Position, Vector3 Target, Vector3 UpVector,
            float FieldOfView, float NearPlaneDistance, float FarPlaneDistance, float AspectRatio,
            Vector3 Offset, Matrix Orientation)
            : base(Position, Target, UpVector, FieldOfView, NearPlaneDistance, FarPlaneDistance, AspectRatio)
        {
            this.Offset = Offset;
            this.Orientation = Orientation;

            this.halfShakePhaseTimeInv = 1.0f / ((float)shakePhaseTimeMs * 0.5f);
        }

        public void HookOn(GameObject node)
        {
            HookedNode = node;
        }

        public override void Update(GameTime gameTime)
        {
            Vector3 offset = Vector3.Zero;
            if (shaking)
            {
                elapsedShakeTimeMs += gameTime.ElapsedGameTime.Milliseconds;

                int phase = (currentShakeTimeMs - elapsedShakeTimeMs) / shakePhaseTimeMs;
                float phaseProgress = (float)(elapsedShakeTimeMs % shakePhaseTimeMs);
                float maxAmpl = phase * currentShakeStrength * shakeStrengthFactor;
                float ampl = 1.0f - Math.Abs(shakePhaseTimeMs * 0.5f - phaseProgress) * halfShakePhaseTimeInv;

                Vector3.Multiply(ref currentShakeAxis, ampl * maxAmpl, out offset);

                if (elapsedShakeTimeMs > currentShakeTimeMs)
                {
                    shaking = false;
                }
            }

            Position = HookedNode.Position - Vector3.Transform(Offset,
                Matrix.CreateRotationY(HookedNode.Rotation.Y)) + offset;
            Target = HookedNode.Position + 12000 * Vector3.Transform(Vector3.Forward, Matrix.CreateRotationY(HookedNode.Rotation.Y)) + offset;

            UpdateViewMatrix();
        }

        public override void Shake(float strength)
        {
            if (shaking && strength < currentShakeStrength)
                return;

            shaking = true;
            if (strength > 100.0f)
                strength = 100.0f;
            if (strength < 0.0f)
                strength = 0.0f;
            currentShakeStrength = strength;
            currentShakeTimeMs = shakeMaxTimeMs;
            currentShakeAxis = RandomHelper.RandomVector();
            elapsedShakeTimeMs = 0;
        }
    }
}

